package com.yswy.app.service.impl;

import com.auth0.jwt.interfaces.Claim;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yswy.app.dto.AppUserUpdateReqVO;
import com.yswy.app.dto.LoginUserDto;
import com.yswy.app.dto.domianVo.AppUserVo;
import com.yswy.app.enums.RedisHoldKeyEnums;
import com.yswy.app.service.*;
import com.yswy.common.enums.DateUnitEnums;
import com.yswy.common.exception.LoginException;
import com.yswy.common.model.ResultModel;
import com.yswy.common.utils.DateUtils;
import com.yswy.common.utils.StringUtils;
import com.yswy.common.utils.TokenTool;
import com.yswy.common.utils.bloom.BloomRedisService;
import com.yswy.common.utils.redis.RedisConstants;
import com.yswy.common.utils.redis.RedisUtil;
import com.yswy.common.utils.sms.CjSmsUtil;
import com.yswy.common.utils.sms.CnyzSmsUtil;
import com.yswy.domain.common.BasePojo;
import com.yswy.domain.entity.*;
import com.yswy.domain.mapper.AppUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.zip.CRC32;


@Slf4j
@Service
public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {

    @Autowired
    private RedisUtil redisUtil ;
    @Autowired
    private SysConfigService configService;
    @Value("${token.sk}")
    private String tokenSk;
    private static final int INVATE_CODE_SIZE = 7;
    @Value("${pagkename1}")
    private String pagekname1;
    @Autowired
    private BloomRedisService bloomRedisService;
    @Autowired
    private CjSmsUtil cjSmsUtil;
    @Autowired
    private CnyzSmsUtil cnyzSmsUtil;
    @Autowired
    private TgCallBackIdService tgCallBackIdService;
    @Autowired
    private TgCallBackKsIdService tgCallBackKsIdService;
    @Autowired
    private TgCallBackHdIdService tgCallBackHdIdService;
    @Autowired
    private TgChannleService tgChannleService;
    @Autowired
    private UserDeviceInfoServiceImpl userDeviceInfoService;
    @Autowired
    private PackageManageService packageManageService;

    @Autowired
    private UserLoginLogService userLoginLogService;
    @Autowired
    private UserWxOpenService userWxOpenService;
    @Autowired
    private UserMoneyService userMoneyService;
    @Autowired
    private IpPlaceService ipPlaceService;
    ExecutorService exec = Executors.newCachedThreadPool();

    private String DEL_MSG = "登录失败，您的账户已注销";
    @Override
    public ResultModel sendCode(String phone) {
        if(StringUtils.isBlank(phone)){
            throw new IllegalArgumentException("请输入手机号码");
        }
        Long phoneCache = redisUtil.getExpire(RedisConstants.SMS_SEND_PHONE + phone);
        if (StringUtils.isNotNull(phoneCache) && phoneCache > 0) {
            throw new IllegalArgumentException("请" + phoneCache + "秒之后再试!");
        }
        // 产生随机验证码
        String smsCode = StringUtils.createCode();
        log.info(phone + "验证码  " + smsCode);
        Integer usdSMSCode = Integer.parseInt(configService.selectConfigByKey("sys.SMS.code"));
        if (usdSMSCode == 1){
            cjSmsUtil.sendCodeMsg(phone,smsCode , pagekname1);
        }else {
            cnyzSmsUtil.sendCode(phone,smsCode , pagekname1);
        }
        redisUtil.setForObjectTimeCustom(RedisConstants.SMS_AGENT_LOGIN, smsCode, 5, TimeUnit.MINUTES);
        redisUtil.setForObjectTimeCustom(RedisConstants.SMS_SEND_PHONE + phone, smsCode, 60, TimeUnit.SECONDS);
        return ResultModel.ok();
    }
    @Override
    public ResultModel loginProgram(LoginUserDto loginUserDto , String ip , String channle , String v) {
        //Token对象
        Map<String, String> seeds = new HashMap<>();
        String token = null ;
        UserWxOpen userWxOpen = userWxOpenService.wxOauth(loginUserDto.getCode());
        AppUser appUser = getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getOpenid , userWxOpen.getOpenid()));
        if (appUser == null){//新用户没有登录
            appUser = new AppUser();
            appUser.setCreateTime(new Date());
            appUser.setOpenid(userWxOpen.getOpenid());
            appUser.setSessionKey(userWxOpen.getSessionKey());
            appUser.setNickName("用户"+StringUtils.createName());
            appUser.setHeadImg("https://imgeffect.obs.cn-north-1.myhuaweicloud.com/ImgEffect/imgeffectbanner/headimg.png");
            appUser.setCreateIp(ip);
            appUser.setSex(1);
            save(appUser);
            appUser = getById(appUser.getId());
            //生成邀请码
            checkAndGenerateInvateCode(appUser.getId());
            AppUser upUser = invateRelation(appUser,loginUserDto.getInvatecode());
            if(null != upUser) {
                //邀请奖励
            }
            //绑定用户渠道
            PackageManage packageManage =  packageManageService
                    .gePackageManageByChannle(loginUserDto.getChannel());
            if (packageManage != null) {
                appUser.setChannel(packageManage.getChannel());
                appUser.setPackageManageId(packageManage.getId());
            }
            //判断登录回传
            try {
                //判断用户来源
                Integer integer =  tgChannleService.getUserSource( appUser.getId() ,ip , 0);
                if (integer == 0){
                    tgCallBackIdService.callBackLogin(appUser.getId() , ip);
                }else if (integer == 1){
                    tgCallBackKsIdService.ksCallBackLogin(appUser.getId() , ip);
                }else if (integer == 2){
                    tgCallBackHdIdService.hdCallBackLogin(appUser.getId() , ip);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }else {
            if (appUser.getIsDel() == 1){
                return new ResultModel(-1005, DEL_MSG);
            }
            appUser.setUpdateTime(new Date());
        }

        seeds.put("id", appUser.getId().toString());
        token = TokenTool.crtToken(tokenSk, seeds, Calendar.DATE, 30);
        redisUtil.setForObjectTimeCustom("TOKEN:"+appUser.getId(), token, 30, TimeUnit.DAYS);
        appUser.setAuthToken(token);
        updateById(appUser);

        userLoginLogService.addUserLoginLog(appUser.getId() ,channle , v ,ip );
        appUser.setIsMember(vipBecomeDue(null ,appUser));
        UserMoney userMoney = userMoneyService.getUserMoney(appUser.getId());


        ipPlaceService.getIpPlaceStatus(ip);
        return ResultModel.ok(new AppUserVo(appUser,userMoney.getMoneyDec() , false));
    }




    @Override
    public ResultModel loginApp(LoginUserDto loginUserDto , String ip , String channle , String v) {

//        System.out.println(loginUserDto.getUserDeviceInfo().toString());
        //Token对象
        Map<String, String> seeds = new HashMap<>();
        String token = null ;
//        String deviceId = null ;
        //查询用户是否存在
        UserDeviceInfo userDeviceInfo = new UserDeviceInfo(loginUserDto.getUserDeviceInfo());

        LambdaQueryWrapper<UserDeviceInfo> lambdaQueryWrapper = new LambdaQueryWrapper<UserDeviceInfo>() ;

        if (userDeviceInfo.getSystemDevice().equals("android")){
            if (StringUtils.isNotEmpty(userDeviceInfo.getOaid())){
                lambdaQueryWrapper.eq(UserDeviceInfo::getOaidCrc ,userDeviceInfo.getOaidCrc());
            }else if (StringUtils.isNotEmpty(userDeviceInfo.getDeviceId())){
                lambdaQueryWrapper.eq(UserDeviceInfo::getDeviceIdCrc ,userDeviceInfo.getDeviceIdCrc());
            }else {
                throw new LoginException("登录失败");
            }
        }else if (userDeviceInfo.getSystemDevice().equals("ios")){
            if (StringUtils.isNotEmpty(userDeviceInfo.getIdfa())){
                lambdaQueryWrapper.eq(UserDeviceInfo::getIdfaCrc ,userDeviceInfo.getIdfaCrc());
            }else {
                throw new LoginException("登录失败");
            }
        }else {
            throw new LoginException("登录失败");
        }

        AppUser appUser = new AppUser();
        UserDeviceInfo appUserDeviceInfoOld = userDeviceInfoService.getOne(lambdaQueryWrapper.last("LIMIT 1"));
        Boolean isNewUser = false ;

        if (appUserDeviceInfoOld == null){//新设备没有登录
            //生成 Token
            isNewUser = true;
            appUser.setCreateTime(new Date());
            appUser.setNickName("用户"+StringUtils.createName());
            appUser.setHeadImg("https://imgeffect.obs.cn-north-1.myhuaweicloud.com/ImgEffect/imgeffectbanner/headimg.png");
            appUser.setCreateIp(ip);
            appUser.setSex(1);
            save(appUser);
            appUser = getById(appUser.getId());
//            goldTaskService.addByRegister( appUser.getId());
            AppUser upUser = invateRelation(appUser,loginUserDto.getInvatecode());
            if(null != upUser) {
                //邀请奖励
            }
            //绑定用户设备
            userDeviceInfo.setUserId(appUser.getId());
            userDeviceInfoService.save(userDeviceInfo);
            appUser.setDeviceId(userDeviceInfo.getId()+"");
            //判断登录回传
            try {
                //判断用户来源
//                Integer integer =  tgChannleService.getUserSource(userDeviceInfo, ip , 0);
//                if (integer == 0){
//                    tgCallBackIdService.callBackLogin(userDeviceInfo , ip);
//                }else if (integer == 1){
//                    tgCallBackKsIdService.ksCallBackLogin(userDeviceInfo , ip);
//                }
                PackageManage packageManage =  packageManageService
                        .gePackageManageByChannle(loginUserDto.getChannel());
                if (packageManage != null) {
                    appUser.setChannel(packageManage.getChannel());
                    appUser.setPackageManageId(packageManage.getId());
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }else {//老设备
            appUser = getById(appUserDeviceInfoOld.getUserId());
            if (appUser.getIsDel() == 1){
                return new ResultModel(-1005, DEL_MSG);
            }
            appUser.setUpdateTime(new Date());
        }

        seeds.put("id", appUser.getId().toString());
        token = TokenTool.crtToken(tokenSk, seeds, Calendar.DATE, 30);
        redisUtil.setForObjectTimeCustom("TOKEN:"+appUser.getId(), token, 30, TimeUnit.DAYS);
        appUser.setAuthToken(token);
        updateById(appUser);



        userLoginLogService.addUserLoginLog(appUser.getId() ,channle , v ,ip );
        appUser.setIsMember(vipBecomeDue(null ,appUser));
        UserMoney userMoney = userMoneyService.getUserMoney(appUser.getId());
        ipPlaceService.getIpPlaceStatus(ip);
        return ResultModel.ok(new AppUserVo(appUser,userMoney.getMoneyDec() , false));
    }

    @Override
    public ResultModel getUserInfo(Long userId) {
        AppUser appUser = getById(userId) ;
        UserMoney userMoney = userMoneyService.getUserMoney(appUser.getId());
        appUser.setIsMember(vipBecomeDue(null ,appUser));
//        Boolean is = ipPlaceService.getIpPlaceStatus(appUser.getCreateIp());
        return ResultModel.ok(new AppUserVo(appUser,userMoney.getMoneyDec() , false));
    }


    @Override
    public AppUser getUserByAuthToken(String token) {
        if (StringUtils.isEmpty(token)){
            return null;
        }
        if ("root".equals(token)) {
            return list(new LambdaQueryWrapper<AppUser>().eq(AppUser::getAuthToken, token)).get(0);
        }
        Map<String, Claim> seedss = null ;
        try{
            seedss = TokenTool.verifyToken(token,tokenSk);
        }catch (Exception e){
            throw new LoginException("登录过期请重新登录");
        }
        String uid = seedss.get("id").asString() ;
        AppUser user = getOne(new QueryWrapper<AppUser>().lambda()
                .eq(AppUser::getId, uid));
        if (user != null){

            if (user.getIsDel() != 0){
//                throw new IllegalArgumentException(DEL_MSG);
                return null;
            }
        }

        return user ;
    }

    @Override
    public void updateUserDevice() {
        List<UserDeviceInfo> userDeviceInfos = userDeviceInfoService.list(new LambdaQueryWrapper<UserDeviceInfo>().last("limit 15500,16049"));
        for (UserDeviceInfo us:userDeviceInfos
             ) {
            System.out.println(us.getId());

            if (StringUtils.isNotEmpty(us.getDeviceId())){
                CRC32 crc32 = new CRC32();
                crc32.update(us.getDeviceId().getBytes());
                us.setDeviceIdCrc(crc32.getValue());
            }else {
                us.setDeviceIdCrc(null);
            }
            if (StringUtils.isNotEmpty(us.getOaid())){
                System.out.println(us.getOaid());
                CRC32 crc32 = new CRC32();
                crc32.update(us.getOaid().getBytes());
                us.setOaidCrc(crc32.getValue());
            }else {

                userDeviceInfoService.update(new LambdaUpdateWrapper<UserDeviceInfo>().eq(BasePojo::getId , us.getId()).set(UserDeviceInfo::getOaidCrc , null));
            }
            userDeviceInfoService.updateById(us);
        }
    }

    @Override
    public void shielding(Long userId) {
        AppUser user = getOne(new QueryWrapper<AppUser>().lambda()
                .eq(AppUser::getId, userId));
        user.setIsDel(1);
        updateById(user);
    }

    @Override
    public void updateUserSex(Integer sex, Long userId) {
        AppUser user = getOne(new QueryWrapper<AppUser>().lambda()
                .eq(AppUser::getId, userId));

        if (user != null){
            user.setSex(sex);
        }
        updateById(user);
    }


    @Override
    public void updateInfo(AppUserUpdateReqVO req, Long userId) {
        String nickName = req.getNickName();
        String headImg = req.getHeadImg();
        String userPhone = req.getUserPhone();
        boolean notEmptyNickName = StringUtils.isNotEmpty(nickName);
        boolean notEmptyHeadImg = StringUtils.isNotEmpty(headImg);
        boolean notEmptyUserPhone = StringUtils.isNotEmpty(userPhone);
        Assert.state(notEmptyNickName || notEmptyHeadImg ||notEmptyUserPhone , "未有需要修改的信息");
        AppUser appUser = baseMapper.selectById(userId);
        if (notEmptyNickName) {
            appUser.setNickName(nickName);
        }
        if (notEmptyHeadImg) {
            appUser.setHeadImg(headImg);
        }
        if (notEmptyUserPhone) {
            Integer count = count(new LambdaQueryWrapper<AppUser>().eq(AppUser::getUserPhone , userPhone));
            if (count>0){
                throw new IllegalArgumentException("该手机号已被绑定");
            }
            //验证验证码
            String cacheSmsCode = redisUtil.get(RedisConstants.SMS_SEND_PHONE + userPhone);
            log.info("验证码内容：{}" , cacheSmsCode);
            if (StringUtils.isBlank(cacheSmsCode) || !cacheSmsCode.equals(req.getSmsCode())) {
                log.info("输入验证码：{}",req.getSmsCode());
                throw new IllegalArgumentException("验证码有误");
            }
            appUser.setUserPhone(userPhone);
        }
        baseMapper.updateById(appUser);
    }


    @Override
    public boolean vipBecomeDue(Long userId ,AppUser appUser) {
        if (appUser == null){
            appUser = getById(userId) ;
        }
        if (appUser.getMemberExpirationTime() == null){
            return false;
        }
        if (appUser.getMemberExpirationTime().compareTo(new Date()) >0 ){
            return true ;
        }else {
            return false ;
        }
    }

    @Override
    public void addVipTime(Long userId, VipPrice vipPrice) {
        AppUser appUser = getById(userId) ;
        Date vipDateNow = new Date();
        if ( appUser.getMemberExpirationTime() != null){
            vipDateNow = appUser.getMemberExpirationTime().before(new Date())?new Date():appUser.getMemberExpirationTime();
        }
        vipDateNow = DateUtils.addDateNum(vipDateNow,vipPrice.getVipTimeType(),vipPrice.getVipTime().intValue());

        if (vipPrice.getVipTime() >=99){
            try {
                appUser.setMemberExpirationTime(
                        DateUtils.getSimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS)
                                .parse("2099-09-09 00:00:00"));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }else {
            appUser.setMemberExpirationTime(vipDateNow);
        }
        updateById(appUser);
    }

    @Override
    public void addVipTime( AgreementOrder agreementOrder) {
        AppUser appUser = getById(agreementOrder.getUserId()) ;
        Date vipDateNow = appUser.getMemberExpirationTime() == null?new Date():appUser.getMemberExpirationTime();
        if (agreementOrder.getPeriodType().equals(DateUnitEnums.DAY.getMessage())){
            vipDateNow = DateUtils.addDateNum(vipDateNow,DateUnitEnums.DAY.getMessage(), agreementOrder.getPeriod().intValue());
        }else if (agreementOrder.getPeriodType().equals(DateUnitEnums.MONTH.getMessage())){
            vipDateNow = DateUtils.addDateNum(vipDateNow,DateUnitEnums.MONTH.getMessage(),agreementOrder.getPeriod().intValue());
        }
        appUser.setMemberExpirationTime(vipDateNow);
        updateById(appUser);
    }

    @Override
    public void addVipTime(Long userId, AgreementOrderPayLog agreementOrderPayLog) {
        AppUser appUser = getById(userId) ;
        Date vipDateNow = appUser.getMemberExpirationTime() == null?new Date():appUser.getMemberExpirationTime();
        if (agreementOrderPayLog.getAgreemenTimeType().equals(DateUnitEnums.DAY.getMessage())){
            vipDateNow = DateUtils.addDateNum(vipDateNow,DateUnitEnums.DAY.getMessage(), agreementOrderPayLog.getAgreemenTime().intValue());
        }else if (agreementOrderPayLog.getAgreemenTimeType().equals(DateUnitEnums.MONTH.getMessage())){
            vipDateNow = DateUtils.addDateNum(vipDateNow,DateUnitEnums.MONTH.getMessage(),agreementOrderPayLog.getAgreemenTime().intValue());
        }
        appUser.setMemberExpirationTime(vipDateNow);
        updateById(appUser);
    }

    /**
     * 邀请关联
     * @param invatecode
     * @return
     */
    private AppUser invateRelation(AppUser appUser ,String invatecode) {
        AppUser upUser = getUpUser(invatecode);
        if(upUser!=null) {
            appUser.setUpUserId(upUser.getId());
            appUser.setUpUserId(upUser.getId());
            updateById(appUser);
        }
        return upUser;
    }

    /**
     * 获取上级用户
     * @param invatecode
     * @return
     */
    private AppUser getUpUser(String invatecode) {
        AppUser upUser = null == invatecode?null: getUpUserByInvatecode(invatecode);
        return upUser;
    }

    /**
     * 通过邀请码找到上级
     * @param invatecode
     * @return
     */
    private AppUser getUpUserByInvatecode(String invatecode) {
        return getOne(new LambdaQueryWrapper<AppUser>()
                .eq(AppUser::getInvatecode , invatecode));
    }

    /**
     * 监测并生成个人邀请码
     */
    private void checkAndGenerateInvateCode(Long userId) {
        exec.submit(()->{
            AppUser appUserInfo = getById(userId);
            try {
                String invatecode = appUserInfo.getInvatecode();
                if(null!=invatecode)
                    return;
                invatecode = getInvateCode();
                appUserInfo.setInvatecode(invatecode);
                updateById(appUserInfo);
                bloomRedisService.addByBloomFilter(RedisHoldKeyEnums.BLOOM_INVITE_CODE.getCode(), invatecode);
            }catch(Exception e) {
                log.error("用户"+appUserInfo.getId()+"邀请码失败", e);
            }
        });
    }

    private String getInvateCode() {
        String invatecode = null;
        boolean exist = true;
        while(exist) {
            invatecode = genInvateCode();
            exist = bloomRedisService.includeByBloomFilter(RedisHoldKeyEnums.BLOOM_INVITE_CODE.getCode(), invatecode);
        }
        return invatecode;
    }

    private String genInvateCode() {
        String cs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        Random ran = new Random();
        int clen = cs.length();
        StringBuilder stringBuilder = new StringBuilder(4);
        for(int i=0;i<INVATE_CODE_SIZE;i++) {
            boolean isNum = ran.nextBoolean();
            if(isNum)
                stringBuilder.append(ran.nextInt(10));
            else
                stringBuilder.append(cs.charAt(ran.nextInt(clen)));
        }
        return stringBuilder.toString();
    }

    private List<String> loginWhiteListPhone() {
        String phones = configService.selectConfigByKey("login.white-list.phone");
        phones = phones.replaceAll(" ", "");
        String[] split = phones.split(",");
        return Arrays.asList(split);
    }
}
